package org.myspringframework.core.aop;

import lombok.Getter;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.myspringframework.core.aop.aspect.AspectWrapper;
import org.myspringframework.core.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author jaymin.<br>
 * 切面执行逻辑执行器.<br>
 * 支持按顺序加载Aspect.<br>
 * 2021/2/15 22:02
 */
public class AspectListExecutor implements MethodInterceptor {
    /**
     * 被代理的类
     */
    private Class<?> targetClass;
    /**
     * 包含执行顺序的切面类列表
     */
    @Getter
    private List<AspectWrapper> sortedAspectWrapperList;

    public AspectListExecutor(Class<?> targetClass, List<AspectWrapper> aspectWrapperList) {
        this.targetClass = targetClass;
        this.sortedAspectWrapperList = sortAspectWrapperList(aspectWrapperList);
    }

    /**
     * 对切面包装类按Order进行排序,order越小，切面逻辑越早执行
     *
     * @param aspectWrapperList 当前被代理类所声明的切面类
     * @return
     */
    private List<AspectWrapper> sortAspectWrapperList(List<AspectWrapper> aspectWrapperList) {
        Collections.sort(aspectWrapperList, Comparator.comparingInt(AspectWrapper::getOrderIndex));
        return aspectWrapperList;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object returnValue = null;
        if (CollectionUtils.isEmpty(sortedAspectWrapperList)) {
            return returnValue;
        }
        // 1.按照order的顺序升序执行完所有Aspect的before方法
        invokeBeforeAdvices(method, args);
        // 2.执行被代理类的方法
        try {
            returnValue = methodProxy.invokeSuper(proxy, args);
            // 3.如果被代理方法正常返回，则按照order的顺序降序执行完所有Aspect的afterReturning方法
            returnValue = invokeAfterReturningAdvices(method, args, returnValue);
        } catch (Exception exception) {
            // 4.如果被代理方法抛出异常，则按照order的顺序降序执行完所有Aspect的afterThrowing方法
            invokeAfterThrowingAdvices(method, args, exception);
        }
        return returnValue;
    }

    private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception exception) throws Throwable {
        for (int i = sortedAspectWrapperList.size() - 1; i >= 0; i--) {
            sortedAspectWrapperList.get(i)
                    .getAspectObject()
                    .afterThrowing(targetClass, method, args, exception);
        }
    }

    /**
     * 如果被代理方法正常返回，则按照order的顺序降序执行完所有Aspect的afterReturning方法
     *
     * @param method      被代理方法
     * @param args        方法参数
     * @param returnValue 返回值
     * @return
     */
    private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
        Object result = null;
        for (int i = sortedAspectWrapperList.size() - 1; i >= 0; i--) {
            result = sortedAspectWrapperList.get(i)
                    .getAspectObject()
                    .afterReturning(targetClass, method, args, returnValue);
        }
        return result;
    }

    /**
     * 按照order的顺序升序执行完所有Aspect的before方法
     *
     * @param method 被代理的方法
     * @param args   方法参数
     */
    private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
        for (AspectWrapper aspectWrapper : sortedAspectWrapperList) {
            aspectWrapper.getAspectObject().before(targetClass, method, args);
        }
    }
}
